<!--
@license
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->

<link rel="import" href="../lib/style-util.html">
<link rel="import" href="../lib/resolve-url.html">
<link rel="import" href="../lib/style-transformer.html">
<link rel="import" href="../lib/style-extends.html">
<link rel="import" href="../lib/settings.html">

<script>

  (function() {

    var prepElement = Polymer.Base._prepElement;
    var nativeShadow = Polymer.Settings.useNativeShadow;

    var styleUtil = Polymer.StyleUtil;
    var styleTransformer = Polymer.StyleTransformer;
    var styleExtends = Polymer.StyleExtends;

    Polymer.Base._addFeature({

      _prepElement: function(element) {
        if (this._encapsulateStyle) {
          styleTransformer.element(element, this.is,
            this._scopeCssViaAttr);
        }
        prepElement.call(this, element);
      },

      _prepStyles: function() {
        if (this._encapsulateStyle === undefined) {
          this._encapsulateStyle = !nativeShadow &&
            Boolean(this._template);
        }
        this._styles = this._collectStyles();
        var cssText = styleTransformer.elementStyles(this);
        if (cssText && this._template) {
          var style = styleUtil.applyCss(cssText, this.is,
            nativeShadow ? this._template.content : null);
          // keep track of style when in document scope (polyfill) so we can
          // attach property styles after it.
          if (!nativeShadow) {
            this._scopeStyle = style;
          }
        }
      },

      // search for extra style modules via `styleModules`
      // TODO(sorvell): consider dropping support for `styleModules`
      _collectStyles: function() {
        var styles = [];
        var cssText = '', m$ = this.styleModules;
        if (m$) {
          for (var i=0, l=m$.length, m; (i<l) && (m=m$[i]); i++) {
            cssText += styleUtil.cssFromModule(m);
          }
        }
        cssText += styleUtil.cssFromModule(this.is);
        if (cssText) {
          var style = document.createElement('style');
          style.textContent = cssText;
          // extends!!
          if (styleExtends.hasExtends(style.textContent)) {
            cssText = styleExtends.transform(style);
          }
          styles.push(style);
        }
        return styles;
      },

      // instance-y
      // add scoping class whenever an element is added to localDOM
      _elementAdd: function(node) {
        if (this._encapsulateStyle) {
          // If __styleScoped is set, this is a one-time optimization to
          // avoid scoping pre-scoped document fragments
          if (node.__styleScoped) {
            node.__styleScoped = false;
          } else {
            styleTransformer.dom(node, this.is, this._scopeCssViaAttr);
          }
        }
      },

      // remove scoping class whenever an element is removed from localDOM
      _elementRemove: function(node) {
        if (this._encapsulateStyle) {
          styleTransformer.dom(node, this.is, this._scopeCssViaAttr, true);
        }
      },

      /**
       * Apply style scoping to the specified `container` and all its
       * descendants. If `shoudlObserve` is true, changes to the container are
       * monitored via mutation observer and scoping is applied.
       *
       * This method is useful for ensuring proper local DOM CSS scoping
       * for elements created in this local DOM scope, but out of the
       * control of this element (i.e., by a 3rd-party library)
       * when running in non-native Shadow DOM environments.
       *
       * @method scopeSubtree
       * @param {Element} container Element to scope.
       * @param {boolean} shouldObserve When true, monitors the container
       *   for changes and re-applies scoping for any future changes.
       */
      scopeSubtree: function(container, shouldObserve) {
        if (nativeShadow) {
          return;
        }
        var self = this;
        var scopify = function(node) {
          if (node.nodeType === Node.ELEMENT_NODE) {
            node.className = self._scopeElementClass(node, node.className);
            var n$ = node.querySelectorAll('*');
            Array.prototype.forEach.call(n$, function(n) {
              n.className = self._scopeElementClass(n, n.className);
            });
          }
        };
        scopify(container);
        if (shouldObserve) {
          var mo = new MutationObserver(function(mxns) {
            mxns.forEach(function(m) {
              if (m.addedNodes) {
                for (var i=0; i < m.addedNodes.length; i++) {
                  scopify(m.addedNodes[i]);
                }
              }
            });
          });
          mo.observe(container, {childList: true, subtree: true});
          return mo;
        }
      }

    });

  })();

</script>
